use core::{cell::UnsafeCell, ffi::c_void, marker::PhantomData, mem::MaybeUninit, ptr::null_mut};

use crate::{
    c::rtx_os::{
        osMessageQueueAttr_t, osMessageQueueGet, osMessageQueueId_t, osMessageQueueNew,
        osMessageQueuePut, osMessageQueueReset, osRtxMessageQueue_t, osWaitForever,
    },
    utils::AlignStore,
};

const CB_SIZE: usize = size_of::<osRtxMessageQueue_t>();

unsafe impl <T, const COUNT: usize, const MQ_SIZE: usize> Send for MessageQueue<T, COUNT, MQ_SIZE> {}
unsafe impl <T, const COUNT: usize, const MQ_SIZE: usize> Sync for MessageQueue<T, COUNT, MQ_SIZE> {}


pub struct MessageQueue<T, const COUNT: usize, const MQ_SIZE: usize> {
    cb_mem: AlignStore<CB_SIZE>,
    mq_mem: AlignStore<MQ_SIZE>,
    id: UnsafeCell<osMessageQueueId_t>,
    _p: PhantomData<T>,
}

/**
 * /// Memory size in bytes for Message Queue storage.
/// \param         msg_count     maximum number of messages in queue.
/// \param         msg_size      maximum message size in bytes.
#define osRtxMessageQueueMemSize(msg_count, msg_size) \
  (4*(msg_count)*(3+(((msg_size)+3)/4)))
 */

pub const fn mq_size<T>(count: usize) -> usize {
    4 * count * (3 + ((size_of::<T>() + 3) / 4))
}

impl<T: Copy, const COUNT: usize, const MQ_SIZE: usize> MessageQueue<T, COUNT, MQ_SIZE> {
    
    pub const fn const_new() -> Self {
        Self {
            cb_mem: AlignStore::const_new(),
            mq_mem: AlignStore::const_new(),
            id: UnsafeCell::new(null_mut()),
            _p: PhantomData,
        }
    }

    pub unsafe fn create(&self, name: &str) {
        let attr = osMessageQueueAttr_t {
            name: name.as_ptr() as *const i8,
            attr_bits: 0,
            cb_mem: self.cb_mem.raw_ptr(),
            cb_size: CB_SIZE as u32,
            mq_mem: self.mq_mem.raw_ptr(),
            mq_size: MQ_SIZE as u32,
        };
        let id = osMessageQueueNew(COUNT as u32, size_of::<T>() as u32, &attr);
        #[cfg(feature = "assert")]
        assert!(id != null_mut(), "message queue create fail");
        *self.id.get() = id;
    }

    pub unsafe fn post(&self, v: T) -> i32 {
        let buf = &v;
        osMessageQueuePut(*self.id.get(), buf as *const T as _, 0, 0)
    }

    pub unsafe fn poll(&self, item: &mut T, timeout: u32) -> i32 {
        osMessageQueueGet(
            *self.id.get(),
            item as *mut T as _,
            null_mut(),
            timeout,
        )
    }

    pub unsafe fn take(&self) -> T {
        let mut item: MaybeUninit<T> = MaybeUninit::uninit();
        osMessageQueueGet(
            *self.id.get(), 
            item.as_mut_ptr() as _, 
            null_mut(), 
            osWaitForever
        );
        item.assume_init_read()
    }

    pub unsafe fn reset(&self) -> i32 {
        osMessageQueueReset(*self.id.get())
    }
}
